Android IPC知识点记录

Android IPC知识点记录(一、基础概念介绍)

此篇文章是在看完《Android开发艺术探索》后的个人笔记小结,摘录书中大量的语句以及例子,特此声明

先谈谈IPC的使用场景:多进程,多进程的情况分为两种:第一种情况是一个应用因为某些原因需要采用多进程模式实现,第二种情况是当前应用需要向其他应用获取数据。接下来我们就从IPC是什么开始说起……

IPC(Ienter-Process Communication)

含义为进程间通信或者跨进程通信,是指两个进程间进行数据交换的过程(区分进程和线程的关系:包含与被包含)。当然,IPC不是Android中独有的,任何一个操作系统都需要有相应的IPC机制,对于Android来说,它是一种基于Linux内核的移动操作系统,它有自己的进程间通信方式。下面我们来学习多进程的相关知识。

多进程模式

正常情况下,在Android中多进程是指一个应用中存在多个进程的情况。开启方法:给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidMenifest中指定android:process属性,非常规方法:通过JNI在native层去fork一个新的进程。(JNI是什么鬼,待google资料)。多进程命名方式分为两种:一种是进程名以“:”开头的进程,它的完整进程名是当前的进程名前面附加上当前的包名,第二种是完整的命名方式;两者的区别:进程名以“:”开头的进程名属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,而另一种命名方式属于全局进程,其他应用通过SharedUID方式可以和它跑在同一个进程中。###Android会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据,两个应用通过SharedUID跑在同一个进程中是有要求的,需要两个应用具有相同的SharedUID并且签名相同才可以。在这种情况下,它们可以互相访问对方的私有数据。(SharedUID番外篇:http://www.cnblogs.com/mythou/p/3258715.html)

多进程模式的运行机制

我们知道Android为每一个应用分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机。而不同虚拟机在内存分配上有不同的地址空间,这就导致了在不同的虚拟机访问同一个类的对象会产生多份副本:所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败,这也就是多进程带来的主要影响。一般来说,使用多进程会造成如下几方面的问题:

  1. 静态成员和单例模式会完全失效
  2. 线程同步机制完全失效
  3. SharedPreferences的可靠性下降
  4. Application会多次创建
    总结:在多进程模式中,不同进程的组件会拥有独立的虚拟机,Application以及内存空间。

IPC的基础知识

对象的序列化:Android中存在两种序列化方式:第一种是Serializable接口,是Java所提供的一个序列化接口,第二种是Parcelable,是Android中的序列化方式。

Serializable接口

实现Serializable只需要这个类实现Serializable接口并声明一个serialVersionUID既可,这个值可以用当前类的hash值,也可以自行指定。
private static final long serialVersionUID = xxxxxL
serialVersionUID的作用:用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常地被反序列化。

1
2
3
4
5
6
7
8
9
//序列化过程
User user = new User(0, "Jake", true);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
//反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User newUser = (User)in.readObject();
in.close();

注意:恢复后的对象newUser和user的内容完全一样,但是两者并不是同一对象(内存不同)。

序列化注意事项:

  1. 静态成员变量属于类不属于对象,所以不会参与序列化过程
  2. 用transient关键字标记的成员变量不参与序列化过程

如果不手动填写serialVersionUID,序列化的时候系统会自动算出一个serialVersionUID写到文件中,然后恢复的时候会再次计算,如果两者的serialVersionUID一致,也就是类没有被修改,那么不会影响反序列化,但是,一旦修改了类,那么计算出来的hash就不同了,反序列化的时候就会报错。程序会crash;如果手动填写,就算修改了类,只要不是毁灭性的修改(例如修改类名),那么反序列化时会尽最大的限度去进行反序列化,最大限度还原对象,而不是直接crash。

Parcelable接口

什么都不说了,先上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Book implements Parcelable{
public int bookId;
public String bookName;

public Book(int bookId, String bookName){
this.bookId = bookId;
this.bookName = bookName;
}

@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(bookId);
dest.writeString(bookName);
}

public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book[] newArray(int size) {
// TODO Auto-generated method stub
return new Book[size];
}

@Override
public Book createFromParcel(Parcel source) {
// TODO Auto-generated method stub
return new Book(source);
}
};

private Book(Parcel in){
bookId = in.readInt();
bookName = in.readString();
}

}
在序列化过程需要实现的功能有序列化、反序列化和内容描述,序列化功能由writeToParcel()来完成,最终是通过Parcel中的一系列write()方法完成。反序列化由CREATOR来完成;内容描述功能由describeContents()完成,几乎所有情况下这个方法都应该返回0(仅当当前对象中存在文件描述符中他才会返回1)。

How to choose?

Serializable是Java的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作;而Parcelable是Android的序列化方式,效率高但是使用起来稍微麻烦点(这是Android推荐的序列化方式,因此我们要首选Parcelable)。